#!/usr/bin/python
#
# Copyright 2011 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Script to upload the client library to PyPI and pack example tarballs.

   Assumes that the user has a configured .pypirc file in their home directory.

Usage:
  $ python pypi_it.py --upload --index pypi --pack adwords --clean
"""

__author__ = 'api.msaniscalchi@gmail.com (Mark Saniscalchi)'

import argparse
import os
import re
import shutil
import subprocess
import sys
import traceback

sys.path.insert(0, os.path.join('..', '..', '..'))


PYPI_SERVERS = {'pypi': 'https://pypi.python.org/pypi',
                'pypi-test': 'https://testpypi.python.org/pypi'}
LIBS = ['adwords', 'adxbuyer', 'dfa', 'dfp']
TARGET_DIR_BASE = '/tmp/google-py'

parser = argparse.ArgumentParser()
parser.add_argument('-c', '--clean', action='store_true',
                    help='If set, removes the temp directory after upload to'
                    'PyPI.')
parser.add_argument('-i', '--index', default='pypi-test',
                    help='The name of the repo. Defaults to "pypi-test".')
parser.add_argument('-p', '--pack',
                    help='Pack examples/docs for the given library. Valid libs:'
                    ' %s or "all".'
                    % LIBS)
parser.add_argument('-u', '--upload', action='store_true',
                    help='If set, uploads to PyPI. Otherwise, just creates'
                    ' directory')


def main(args):
  """Creates temporary directory, updates PyPI, and generates example tarballs.

  Args:
    args: The arguments passed to this program and parsed via argparser.
  """
  source_dir = os.path.abspath('.')
  UpdateSOAPpy()

  # Do not move. SOAPpy needs to be downloaded and patched first.
  from adspygoogle import LIB_PYPI_NAME
  from adspygoogle import LIB_PYPI_URL
  from adspygoogle import LIB_PYPI_VERSION

  lib_tag = 'adspygoogle_v%s' % LIB_PYPI_VERSION
  target_dir = '%s/%s' % (TARGET_DIR_BASE, lib_tag)

  CreateTempDirectory(source_dir, target_dir)

  os.chdir(target_dir)

  os.system('rm pypi_it.py releasetests.sh')

  GenerateDocumentation(LIB_PYPI_NAME, LIB_PYPI_URL)

  GenerateReadme()

  if args.upload:
    UploadToPyPI(args.index)

  CreateTarballs(args.pack)

  if args.clean:
    os.system('rm -rf %s' % target_dir)

  print 'Successfully PyPI-ed!'


def CreateTempDirectory(source_dir, target_dir):
  """Create temporary directory used to store PyPI package and create tarballs.

  Args:
    source_dir: The directory containing the unmodified distribution files.
    target_dir: The temporary directory used for distributing to PyPI.
  """
  # If temp base dir exists, remove it so we start fresh
  if os.path.exists(target_dir):
    os.system('rm -rf %s*' % target_dir)

  shutil.copytree(source_dir, target_dir)

  print 'Built temporary directory: %s' % target_dir


def GenerateDocumentation(name, url):
  """Perform cleanup and then generate the Epydoc documentation.

  Args:
    name: The name of the library.
    url: The website associated with the project.
  """
  os.system('find . \( -name \'*auth.pkl\' -or -name \'*config.pkl\' '
            '-or -name \'*.log\' -or -name \'*.pyc*\''
            ' -or -name \'.project\' -or -name \'.pydevproject\' -or -name '
            '\'.settings\' \) | xargs rm -fr')

  # Intentionally excluding SOAPpy here.
  os.system('epydoc -q --name "%s" --url "%s" --html adspygoogle '
            '--exclude=SOAPpy -o docs' % (name, url))

  os.system('perl -pi -e \'s/Generated by Epydoc (\d+\.\d+\.\d+) .*/Generated '
            'by Epydoc $1/\' docs/*')


def GenerateReadme():
  """Inserts generated content into README.txt."""
  try:
    readme = open('README.txt', 'r')
    data = readme.read()
    readme.close()
  except IOError:
    print 'Can\'t read README.txt.'
    traceback.print_exc()
    sys.exit(1)

  for lib in LIBS:
    if lib is 'adxbuyer':
      continue
    data = InsertCustomDescription(lib, data)

  try:
    readme = open('README.txt', 'w')
    readme.write(data)
    readme.close()
  except IOError:
    print 'Can\'t write to README.txt.'
    traceback.print_exc()
    sys.exit(1)


def InsertCustomDescription(lib_name, data):
  """Locates the library tag and replaces it with the full name and version.

  Args:
    lib_name: The name of the library having a custom description inserted into
             README.txt.
    data: The contents of README.txt that contains the library tag.
  Returns:
    The contents of the file with the inserted custom description.
  """
  return re.sub('(?<=\* )\{\{ %s \}\}' % lib_name,
                GenerateLibraryDescription(lib_name), data)


def GenerateLibraryDescription(lib_name):
  """Generates a library description consisting of its full name and version.

  Args:
    lib_name: The name of the library for which a description is to be
             generated.
  Returns:
    A string description of the given library.
  """
  tmp_lib = __import__('adspygoogle.%s' % lib_name, globals(),
                       locals(), ['LIB_NAME', 'LIB_VERSION'], 0)
  return '%s v%s' % (tmp_lib.LIB_NAME, tmp_lib.LIB_VERSION)


def UpdateSOAPpy():
  """Runs download_and_patch_soappy.py to make sure SOAPpy is up to date.

  Terminates further execution if SOAPpy couldn't be downloaded.

  Raises:
    Exception: An error occured while running download_and_patch_soappy.py.
  """
  cur_dir = os.curdir
  os.chdir(os.path.join(cur_dir, 'adspygoogle', 'SOAPpy'))
  code = subprocess.call(['python', 'download_and_patch_soappy.py'])

  if code:
    raise Exception('Couldn\'t download SOAPpy')

  os.chdir(cur_dir)


def UploadToPyPI(repo):
  """Upload the package to the given PyPI repo using the credentials in .pypirc.

  Args:
    repo: One of the PyPI Index Servers specified in your .pypirc file.
  """
  print 'Uploading to PyPI Repo: %s' % repo
  os.system('python setup.py sdist upload -r %s' % repo)


def CreateTarballs(pack):
  """Create example tarballs.

  Args:
    pack: The library to pack into a tarball. You can specify all libraries by
          passing 'all'.
  """
  if pack == 'all':
    pack_libs = LIBS
  elif pack in LIBS:
    if pack == 'adwords':
      pack_libs = [pack, 'adxbuyer']
    else:
      pack_libs = [pack]
  else:
    print 'Not packing tarballs.'
    return

  os.system('cp adspygoogle/scripts/adspygoogle/common/'
            'generate_refresh_token.py .')

  for lib in pack_libs:
    if lib == 'adxbuyer':
      effective_lib = 'adwords'
    else:
      effective_lib = lib

    os.system('cp adspygoogle/%s/README .' % effective_lib)
    os.system('cp adspygoogle/scripts/adspygoogle/%s/config.py .'
              % effective_lib)
    tmp_lib = __import__('adspygoogle.%s' % effective_lib, globals(),
                         locals(), ['LIB_VERSION'], 0)
    os.system('tar -pczf ../%s_examples_python_%s.tar.gz '
              'examples/adspygoogle/%s docs '
              'config.py '
              'generate_refresh_token.py '
              'README '
              'README.Common '
              'COPYING'
              % (lib, tmp_lib.LIB_VERSION, lib))


if __name__ == '__main__':
  main(parser.parse_args())
